/*
    Seminar Computergrafik SS2012
    Praktikum
    Fachhochschule Münster
    Fachbereich Elektrotechnik und Informatik    
    
    Autoren: Alexander Kuhn & Mark Ruffer
*/

/* strict Modus nutzen */
"use strict";

/* WebGL Objekt */
var gl;

/* Das Canvas Zeichenfeld */
var canvas;

/* Texturen */
var textureBody;
var textureHead;

var vertexTextureCoordBufferHead;
var vertexTextureCoordBufferBody;
var vertexTextureCoordBufferArmsLegs;

/* Speicher für Vertex Positionen */
var vBufferBody;
var vBufferLeftLeg;
var vBufferRightLeg;
var vBufferLeftArm;
var vBufferRightArm;
var vBufferHead;

var vLightSourceBuffer;

/* Speicher für Farbinformationen */
// NOT IN USE

/* NOT IN USE
var cBufferStandardCube = [
    [0.5,   0.5,    0.5,    1.0],
    [0.5,   0.5,    0.5,    1.0],
    [0.5,   0.5,    0.5,    1.0],
    [0.5,   0.5,    0.5,    1.0],
    [0.5,   0.5,    0.5,    1.0],
    [0.5,   0.5,    0.5,    1.0]
];
*/

/* Licht */
var vertexNormalsBuffer;

/* Speicher für Indexinformationen */
var iBuffer;

/* Standardwerte für Würfelindizes */
var iBufferStandardCube = [
    0, 1, 2,      0, 2, 3,     /* front */ 
    4, 5, 6,      4, 6, 7,     /* hinten */
    8, 9, 10,     8, 10, 11,   /* oben */  
    12, 13, 14,   12, 14, 15,  /* unten */ 
    16, 17, 18,   16, 18, 19,  /* rechts */
    20, 21, 22,   20, 22, 23   /* links */ 
];

/* Buffer für das Koordinatensystem */
var vertexAxisXBuffer;
var vertexAxisYBuffer;
var vertexAxisZBuffer;

/* Matrizen */
var pMatrix = mat4.create();      /* Projektionsmatrix */
var mvMatrix = mat4.create();     /* Model-View-Matrix */

/* Stacks */
var mvMatrixStack = [];           /* Stack für die mvMatrix */

/* Shaderprogramme */
var shaderProgram;

/* Animationsvariablen */
var rRobotX = 0;                    
var rRobotY = 0;
var rLegs = 0;
var rArms = 0;
var rHead = 0;
var rTorso = 0;
var lastTime = 0;
var plusitLegs = true;
var plusitArms = true;
var plusitHead = true;
var plusitTorso = true;

var rotateRobotX = 0;
var rotateRobotY = 0;
var zoom = -80;

var rotateSceneY = 0;
var rotateSceneX = 0;
var rotateSceneZ = 0;

var keyPressed = []; 

/**
 * Setzt den Drehwinkel so, dass 90° bzw 45° pro Sekunde gedreht werden. Es wird
 * nicht in jedem Durchlauf ein fester Wert gedreht, sondern Abhängig von der ver-
 * gangenen Zeit, sodass die Animation pro Zeit fest rotiert.
 * 
 * TODO: geht das effizienter ?    
 */ 
function animate(){
    var timeNow, elapsed, interm;
    timeNow = new Date().getTime();
    if(lastTime != 0){
        elapsed = timeNow - lastTime;
        rRobotX += (rotateRobotX * elapsed)/1000.0;
        rRobotY += (rotateRobotY * elapsed)/1000.0;
        
        /* Beine bewegen */
        interm = (90 * elapsed)/1000.0;
        if(plusitLegs){
            rLegs += interm;              
        } else {
            rLegs -= interm;
        }
        if(rLegs >= 33.0) plusitLegs = false;
        else if (rLegs <= -33.0) plusitLegs = true;
        
        /* Arme bewegen */
        interm = (60 * elapsed)/1000.0;
        if(plusitArms)  rArms += interm;
        else            rArms -= interm;
        if(rArms >= 75.0) plusitArms = false;
        else if(rArms <= -25.0) plusitArms = true;
        
        /* Kopf bewegen */
        interm = (45 * elapsed)/1000.0;
        if(plusitHead)  rHead += interm;
        else            rHead -= interm;
        if(rHead >= 45.0)   plusitHead = false;
        else if(rHead <= -45.0) plusitHead = true;

        /* Torso (Kopf, Arme und Körper) bewegen */        
        interm = (45 * elapsed)/1000.0;
        if(plusitTorso) rTorso += interm;
        else            rTorso -= interm;
        if(rTorso >= 0.0)   plusitTorso = false;
        else if(rTorso <= -45.0) plusitTorso = true;
    }
    lastTime = timeNow;
}

/**
 * Initialisiert die Shader
 */ 
function initShaders(){
    var fragmentShader, vertexShader;
    
    fragmentShader = getShader(gl, gl.FRAGMENT_SHADER, shaderprogram_fragmentshader);
    vertexShader = getShader(gl, gl.VERTEX_SHADER, shaderprogram_vertexshader);

    /* das shaderProgram kann je einen fragment und vertex shader beherbergen */
    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
      window.alert("Could not initialise shaders");
    }

    gl.useProgram(shaderProgram);

    /* 
        die position eines vertex wird als attribut an den shader geliefert.
        dazu wird die speicheradresse dieser shader variablen als zusätzliche
        eigenschaft an das shaderprogram angehangen.
    */   
    shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
    
    /* adresse für attribut aVertexColor speichern */
    /* KEINE FARBEN MEHR -> TEXTUREN
    shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
    gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
    */
    
    /* adresse für attribut aTextureCoord speichern */
    shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
    gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);
    
    shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal");
    gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute);

    /* adressen der Uniforme speichern */    
    shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
    shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
    shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
    shaderProgram.nMatrixUniform = gl.getUniformLocation(shaderProgram, "uNMatrix");
    shaderProgram.ambientColorUniform = gl.getUniformLocation(shaderProgram, "uAmbientColor");
    shaderProgram.pointingLightPositionUniform = gl.getUniformLocation(shaderProgram, "uPointingLightPosition");
    shaderProgram.pointingLightColorUniform = gl.getUniformLocation(shaderProgram, "uPointingLightColor");
    shaderProgram.useTextureUniform = gl.getUniformLocation(shaderProgram, "uUseTexture");
    shaderProgram.vertexColorUniform = gl.getUniformLocation(shaderProgram, "uVertexColor");
}

/**
 * Initialisiert die Speicherblöcke
 */ 
function initBuffers(){
    var vertices;
    var axisVertices;
    
    /* X-Achse mit Pfeilspitze*/
    axisVertices = [
        -30.0,   0.0,    0.0,
        30.0,    0.0,    0.0,
        29.0,    1.0,    0.0,
        29.0,    -1.0,   0.0,
        30.0,    0.0,    0.0
    ];
    vertexAxisXBuffer = initBuffer(gl, gl.ARRAY_BUFFER, axisVertices, gl.STATIC_DRAW, 5, 3, 0);
    
    /* y-Achse mit Rechteckspitze*/
    axisVertices = [
        0.0,    -30.0,  0.0,
        0.0,    30.0,   0.0,
        -1.0,   30.0,   0.0,
        -1.0,   29.0,   0.0,
        1.0,    29.0,   0.0,
        1.0,    30.0,   0.0,
        0.0,    30.0,   0.0
    ];
    vertexAxisYBuffer = initBuffer(gl, gl.ARRAY_BUFFER, axisVertices, gl.STATIC_DRAW, 7, 3, 0);
    
    /* Z-Achse mit Strichspitze */
    axisVertices = [
        0.0,    0.0,    -30.0,
        0.0,    0.0,    30.0,
        0.0,    1.0,    30.0,
        0.0,    -1.0,   30.0
    ];
    vertexAxisZBuffer = initBuffer(gl, gl.ARRAY_BUFFER, axisVertices, gl.STATIC_DRAW, 4, 3, 0);

    var textureCoords = [
      // Front face
      0.0, 0.5,
      0.5, 0.5,
      0.5, 1.0,
      0.0, 1.0,

      // Back face
      0.0, 0.0,
      0.5, 0.0,
      0.5, 0.5,
      0.0, 0.5,

      // Top face
      0.0, 0.0,
      0.5, 0.0,
      0.5, 0.5,
      0.0, 0.5,

      // Bottom face
      0.0, 0.0,
      0.5, 0.0,
      0.5, 0.5,
      0.0, 0.5,

      // Right face
      0.0, 0.0,
      0.5, 0.0,
      0.5, 0.5,
      0.0, 0.5,

      // Left face
      0.0, 0.0,
      0.5, 0.0,
      0.5, 0.5,
      0.0, 0.5
    ];
    
    vertexTextureCoordBufferHead = initBuffer(gl, gl.ARRAY_BUFFER, textureCoords, gl.STATIC_DRAW, 24, 2, 0);
    
    textureCoords = [
      // Front face
      0.0, 0.0,
      0.5, 0.0,
      0.5, 0.5,
      0.0, 0.5,

      // Back face
      0.5, 0.0,
      1.0, 0.0,
      1.0, 0.5,
      0.5, 0.5,

      // Top face
      0.5, 0.0,
      1.0, 0.0,
      1.0, 0.5,
      0.5, 0.5,

      // Bottom face
      0.5, 0.0,
      1.0, 0.0,
      1.0, 0.5,
      0.5, 0.5,

      // Right face
      0.0, 0.5,
      0.0625, 0.5,
      0.0625, 1.0,
      0.0, 1.0,

      // Left face
      0.0, 0.5,
      0.0625, 0.5,
      0.0625, 1.0,
      0.0, 1.0
    ];
    vertexTextureCoordBufferBody = initBuffer(gl, gl.ARRAY_BUFFER, textureCoords, gl.STATIC_DRAW, 24, 2, 0);
    
    textureCoords = [
      // Front face
      0.0, 0.5,
      0.0625, 0.5,
      0.0625, 1.0,
      0.0, 1.0,

      // Back face
      0.0, 0.5,
      0.0625, 0.5,
      0.0625, 1.0,
      0.0, 1.0,

      // Top face
      0.5, 0.0,
      1.0, 0.0,
      1.0, 0.5,
      0.5, 0.5,

      // Bottom face
      0.5, 0.0,
      1.0, 0.0,
      1.0, 0.5,
      0.5, 0.5,

      // Right face
      0.0, 0.5,
      0.0625, 0.5,
      0.0625, 1.0,
      0.0, 1.0,

      // Left face
      0.0, 0.5,
      0.0625, 0.5,
      0.0625, 1.0,
      0.0, 1.0
    ];
    vertexTextureCoordBufferArmsLegs = initBuffer(gl, gl.ARRAY_BUFFER, textureCoords, gl.STATIC_DRAW, 24, 2, 0);
    
    /* Licht */
    /* Normalen für alle Vertizes festlegen */
    var vertexNormals = [
    // Front face
       0.0,  0.0,  1.0,
       0.0,  0.0,  1.0,
       0.0,  0.0,  1.0,
       0.0,  0.0,  1.0,

      // Back face
       0.0,  0.0, -1.0,
       0.0,  0.0, -1.0,
       0.0,  0.0, -1.0,
       0.0,  0.0, -1.0,

      // Top face
       0.0,  1.0,  0.0,
       0.0,  1.0,  0.0,
       0.0,  1.0,  0.0,
       0.0,  1.0,  0.0,

      // Bottom face
       0.0, -1.0,  0.0,
       0.0, -1.0,  0.0,
       0.0, -1.0,  0.0,
       0.0, -1.0,  0.0,

      // Right face
       1.0,  0.0,  0.0,
       1.0,  0.0,  0.0,
       1.0,  0.0,  0.0,
       1.0,  0.0,  0.0,

      // Left face
      -1.0,  0.0,  0.0,
      -1.0,  0.0,  0.0,
      -1.0,  0.0,  0.0,
      -1.0,  0.0,  0.0
    ];
    vertexNormalsBuffer = initBuffer(gl, gl.ARRAY_BUFFER, vertexNormals, gl.STATIC_DRAW, 24, 3, 0);
    
    /* ***** Standard Index Buffer ***** */
    iBuffer = initBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, iBufferStandardCube, gl.STATIC_DRAW, 36, 1, 1);
    
    /* ***** BODY ***** */
    vertices = cubeArray(-10.0, -10.0, 2.5, 20.0, 20.0, 5.0);

    vBufferBody = initBuffer(gl, gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW, 24, 3, 0);
    
    /* ***** LEFT LEG ***** */
    vertices = cubeArray(-10.0, -35.0, 2.5, 5.0, 20.0, 5.0);

    vBufferLeftLeg = initBuffer(gl, gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW, 24, 3, 0);
    
    /* ***** RIGHT LEG ***** */
    vertices = cubeArray(5.0, -35.0, 2.5, 5.0, 20.0, 5.0);
    
    vBufferRightLeg = initBuffer(gl, gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW, 24, 3, 0);
    
    /* ***** LEFT ARM ***** */
    vertices = cubeArray(-15.0, -8.0, 0.5, 2.0, 16.0, 2.0);
    
    vBufferLeftArm = initBuffer(gl, gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW, 24, 3, 0);
    
    /* ***** RIGHT ARM ***** */
    vertices = cubeArray(13.0, -8.0, 2.0, 2.0, 16.0, 2.0);
    
    vBufferRightArm = initBuffer(gl, gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW, 24, 3, 0);
    
    /* ***** HEAD ***** */
    vertices = cubeArray(-5.0, 15.0, 2.5, 10.0, 10.0, 5.0);
    
    vBufferHead = initBuffer(gl, gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW, 24, 3, 0);
    
}

/**
 * Führt die Berechnungen auf der Grafikkarte durch und bringt die erzeugte Grafik auf
 * die Zeichenfläche
 */  
function drawScene(){
    /* Viewport definieren über der Größe des Canvas */
    gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    /* Zeichenfläche leeren */
    gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
    
    /*
        perspektive festlegen:
            45° blickwinkel
            seitenverhältnis des canvas
            nur elemente, die weiter weg sind als 0.1 einheiten
            nur elemente, die näher sind als 100 einheiten
            
    */
    mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 500.0, pMatrix);
    
    /* Einheitsmatrix erzeugen */    
    mat4.identity(mvMatrix);
    
    /* Texturen vorladen */
    bindTextures([gl.TEXTURE0, gl.TEXTURE1], [gl.TEXTURE_2D, gl.TEXTURE_2D], [textureHead, textureBody]);
    
    /* gesamtes Bild verschieben */
    mat4.translate(mvMatrix, [0.0, 5.0, zoom]);
    
    /* ***** Kameradrehung ***** */
    mat4.rotate(mvMatrix, degToRad(rotateSceneY), [0, 1, 0]);
    mat4.rotate(mvMatrix, degToRad(rotateSceneX), [1, 0, 0]);
    mat4.rotate(mvMatrix, degToRad(rotateSceneZ), [0, 0, 1]);
    
    /* ***** Licht ***** */
    /* Umgebungslichtfarbe auslesen und an Shader übergeben */
    gl.uniform3f(shaderProgram.ambientColorUniform, 
                 parseFloat(document.getElementById("LIGHT_ambient_r").value), 
                 parseFloat(document.getElementById("LIGHT_ambient_g").value), 
                 parseFloat(document.getElementById("LIGHT_ambient_b").value)
    );
    /* Lichtposition auslesen */
    var pointingX, pointingY, pointingZ;
    pointingX = parseFloat(document.getElementById("LIGHT_directional_x").value);
    pointingY = parseFloat(document.getElementById("LIGHT_directional_y").value);
    pointingZ = parseFloat(document.getElementById("LIGHT_directional_z").value);
    var adjustedLD = vec3.create([pointingX, pointingY, pointingZ]);
    
    /* Lichtpositionsvektor mit aktueller Drehung multiplizieren */
    mat4.multiplyVec3(mvMatrix, adjustedLD);
    
    /* Pointing Light Position und Farbe an Shader */
    var pointingR, pointingG, pointingB;
    pointingR = parseFloat(document.getElementById("LIGHT_directional_r").value);
    pointingG = parseFloat(document.getElementById("LIGHT_directional_g").value);
    pointingB = parseFloat(document.getElementById("LIGHT_directional_b").value);
    gl.uniform3fv(shaderProgram.pointingLightPositionUniform, adjustedLD);
    gl.uniform3f(shaderProgram.pointingLightColorUniform, pointingR, pointingG, pointingB);        
    
    /***** Achsen zeichnen *****/
    /* Texturen abschalten für Achsen und Farbe einstellen */
    gl.uniform1i(shaderProgram.useTextureUniform, false);
    gl.uniform4f(shaderProgram.vertexColorUniform, 1.0, 1.0, 1.0, 1.0);
    
    /* X-Achse */
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexAxisXBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexAxisXBuffer.itemSize, gl.FLOAT, false, 0, 0);
    setMatrixUniforms();
    gl.drawArrays(gl.LINE_STRIP, 0, vertexAxisXBuffer.numItems);
    
    /* Y-Achse */
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexAxisYBuffer, false);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexAxisYBuffer.itemSize, gl.FLOAT, false, 0, 0);
    setMatrixUniforms();
    gl.drawArrays(gl.LINE_STRIP, 0, vertexAxisYBuffer.numItems);
    
    /* Z-Achse */
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexAxisZBuffer, false);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexAxisZBuffer.itemSize, gl.FLOAT, false, 0, 0);
    setMatrixUniforms();
    gl.drawArrays(gl.LINE_STRIP, 0, vertexAxisZBuffer.numItems);    
    
    /* ***** Lichtquelle zeichnen ***** */
    /* LightSource Buffer */
    // TODO: initBuffer in drawScene eigentlich unerwünscht ... (bufferData() ist langsam)
    var vertices = cubeArray(pointingX, pointingY, pointingZ, 0.5, 0.5, 0.5);
    vLightSourceBuffer = initBuffer(gl, gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW, 24, 3, 0);
    gl.uniform4f(shaderProgram.vertexColorUniform, pointingR, pointingG, pointingB, 1.0);    
    bindAndDraw(gl, shaderProgram, gl.TRIANGLES, vLightSourceBuffer, gl.ARRAY_BUFFER, gl.FLOAT, iBuffer, gl.ELEMENT_ARRAY_BUFFER, gl.UNSIGNED_SHORT, vertexTextureCoordBufferBody, gl.ARRAY_BUFFER, gl.FLOAT, vertexNormalsBuffer, gl.ARRAY_BUFFER, gl.FLOAT, 1);
    
    /* Texturen wieder einschalten */
    gl.uniform1i(shaderProgram.useTextureUniform, true);

    /* Rotation des Roboters */    
    mat4.rotate(mvMatrix, degToRad(rRobotY), [0, 1, 0]);
    mat4.rotate(mvMatrix, degToRad(rRobotX), [1, 0, 0]);

    /* Laden und Zeichnen der Objekte */
    mvPushMatrix();
    mat4.translate(mvMatrix, [0.0, -10.0, 0.0]);
    mat4.rotate(mvMatrix, degToRad(-rTorso), [1, 0, 0]);
    mat4.translate(mvMatrix, [0.0, 10.0, 0.0]);

    bindAndDraw(gl, shaderProgram, gl.TRIANGLES, vBufferBody, gl.ARRAY_BUFFER, gl.FLOAT, iBuffer, gl.ELEMENT_ARRAY_BUFFER, gl.UNSIGNED_SHORT, vertexTextureCoordBufferBody, gl.ARRAY_BUFFER, gl.FLOAT, vertexNormalsBuffer, gl.ARRAY_BUFFER, gl.FLOAT, 1);
    
    mvPushMatrix();    
    mat4.translate(mvMatrix, [0.0, 6.0, 0.0]);
    mat4.rotate(mvMatrix, degToRad(-rArms), [1, 0, 0]);
    mat4.translate(mvMatrix, [0.0, -6.0, 0.0]);
    bindAndDraw(gl, shaderProgram, gl.TRIANGLES, vBufferLeftArm, gl.ARRAY_BUFFER, gl.FLOAT, iBuffer, gl.ELEMENT_ARRAY_BUFFER, gl.UNSIGNED_SHORT, vertexTextureCoordBufferArmsLegs, gl.ARRAY_BUFFER, gl.FLOAT, vertexNormalsBuffer, gl.ARRAY_BUFFER, gl.FLOAT, 1);
    bindAndDraw(gl, shaderProgram, gl.TRIANGLES, vBufferRightArm, gl.ARRAY_BUFFER, gl.FLOAT, iBuffer, gl.ELEMENT_ARRAY_BUFFER, gl.UNSIGNED_SHORT, vertexTextureCoordBufferArmsLegs, gl.ARRAY_BUFFER, gl.FLOAT, vertexNormalsBuffer, gl.ARRAY_BUFFER, gl.FLOAT, 1);
    mvPopMatrix();
    mvPushMatrix();
    mat4.rotate(mvMatrix, degToRad(rHead), [0, 1, 0]);
    bindAndDraw(gl, shaderProgram, gl.TRIANGLES, vBufferHead, gl.ARRAY_BUFFER, gl.FLOAT, iBuffer, gl.ELEMENT_ARRAY_BUFFER, gl.UNSIGNED_SHORT, vertexTextureCoordBufferHead, gl.ARRAY_BUFFER, gl.FLOAT, vertexNormalsBuffer, gl.ARRAY_BUFFER, gl.FLOAT, 0);
    mvPopMatrix();
        
    mvPopMatrix();
    
    mvPushMatrix();
    mat4.translate(mvMatrix, [0.0, -18.0, 0.0]);
    mat4.rotate(mvMatrix, degToRad(rLegs), [1, 0, 0]);
    mat4.translate(mvMatrix, [0.0, 18.0, 0.0]);    
    bindAndDraw(gl, shaderProgram, gl.TRIANGLES, vBufferLeftLeg, gl.ARRAY_BUFFER, gl.FLOAT, iBuffer, gl.ELEMENT_ARRAY_BUFFER, gl.UNSIGNED_SHORT, vertexTextureCoordBufferArmsLegs, gl.ARRAY_BUFFER, gl.FLOAT, vertexNormalsBuffer, gl.ARRAY_BUFFER, gl.FLOAT, 1);
    mvPopMatrix();
    mvPushMatrix();
    mat4.translate(mvMatrix, [0.0, -18.0, 0.0]);
    mat4.rotate(mvMatrix, degToRad(-rLegs), [1, 0, 0]);
    mat4.translate(mvMatrix, [0.0, 18.0, 0.0]);
    bindAndDraw(gl, shaderProgram, gl.TRIANGLES, vBufferRightLeg, gl.ARRAY_BUFFER, gl.FLOAT, iBuffer, gl.ELEMENT_ARRAY_BUFFER, gl.UNSIGNED_SHORT, vertexTextureCoordBufferArmsLegs, gl.ARRAY_BUFFER, gl.FLOAT, vertexNormalsBuffer, gl.ARRAY_BUFFER, gl.FLOAT, 1);
    mvPopMatrix();
    
}

/* QUICK AND DIRTY FPS MEASUREMENT */
var fps_lasttime = 0;
var fps_median = [];
var fps_counter = 0;
function measureFPS(){
    var now, val, sum, i;
    
    now = new Date().getTime();
    if(fps_lasttime!=0){
        val = 1000/(now - fps_lasttime);
        fps_median[fps_counter] = val;
        fps_counter = (fps_counter+1) % 100;
        sum = 0;
        for(i=0; i<100; i++) {
            sum += fps_median[i];
        }
        if(!isNaN(sum/100)) document.getElementById("FPS").value = Math.round(sum/100);
        else document.getElementById("FPS").value = "wait...";
        
    }
    fps_lasttime = now;    
}
/* QUICK AND DIRTY FPS MEASUREMENT */

var mcheck=false;
function tick(){
    /* QUICK AND DIRTY FPS MEASUREMENT */
    if( document.getElementById("FPS_CHECK").checked == true ){
        measureFPS();
        mcheck=true;
    }
    else if(mcheck==true){
        mcheck=false;
        document.getElementById("FPS_CHECK").checked = false;
        document.getElementById("FPS").value = "";
    }
    /* QUICK AND DIRTY FPS MEASUREMENT */
    requestAnimFrame(tick);
    handleKeys();
    drawScene();
    animate();
}

/**
 * Erzeugt ein neues Texturobjekt, erzeugt ein Image Objekt und lädt die
 * angegebene Bilddatei.
 */  
function initTextures(){
    textureBody = gl.createTexture();
    textureBody.image = new Image();
    textureBody.image.onload = function(){
        handleLoadedTexture(textureBody, gl.LINEAR, gl.LINEAR_MIPMAP_NEAREST);
    };
    textureBody.image.src = "body.gif";
    
    textureHead = gl.createTexture();
    textureHead.image = new Image();
    textureHead.image.onload = function(){
        handleLoadedTexture(textureHead, gl.LINEAR, gl.LINEAR_MIPMAP_NEAREST);
    };
    textureHead.image.src = "head.gif";
}

/**
 * Drücken einer Taste eintragen
 * @param event Javascript Event
 */  
function handlekeydown(event){
    keyPressed[event.keyCode] = true;
}

/**
 * Loslassen einer Taste eintragen
 * @param event Javascript Event
 */  
function handlekeyup(event){
    keyPressed[event.keyCode] = false;
}

/**
 * Führe bei jedem Tick entsprechend der gedrückten Tasten Aktionen aus
 */ 
function handleKeys(){
    if(keyPressed[39]){
        rotateRobotY += 5;
        keyPressed[39]=false;
    }
    if(keyPressed[37]){
        rotateRobotY -= 5;
        keyPressed[37]=false;
    }
    if(keyPressed[40]){
        rotateRobotX += 5;
        keyPressed[40]=false;
    }
    if(keyPressed[38]){
        rotateRobotX -= 5;
        keyPressed[38]=false;
    }
    if (keyPressed[33]){
        zoom -= 5;
        keyPressed[33]=false;
    }
    if(keyPressed[34]){
        zoom += 5;
        keyPressed[34]=false;
    }
    if(keyPressed[65]){
        // A
        if(rotateSceneY <= 0){
            rotateSceneY = 359;
        } else {
            rotateSceneY -= 1;
        }        
    }
    if(keyPressed[68]){
        // D
        rotateSceneY = (rotateSceneY + 1) % 360;
    }
    if(keyPressed[87]){
        // W
        rotateSceneX = (rotateSceneX + 1) % 360;
    }
    if(keyPressed[83]){
        // S
        if(rotateSceneX <= 0){
            rotateSceneX = 359;
        } else {
            rotateSceneX -= 1;
        }
    }
    if(keyPressed[81]){
        // Q
        if(rotateSceneZ <= 0){
            rotateSceneZ = 359;
        } else {
            rotateSceneZ -= 1;
        }
    }
    if(keyPressed[69]){
        // E
        rotateSceneZ = (rotateSceneZ + 1) % 360;
    }
    
    
    document.getElementById("GUI_rotate").value = rotateRobotY;
    document.getElementById("GUI_swing").value = rotateRobotX;
    document.getElementById("GUI_zoom").value = zoom;
}

/**
 * Hauptroutine des Programms
 */                 
function main(){
    canvas = document.getElementById("webgl_canvas");
    initGL(canvas);
    initShaders();
    initBuffers();
    initTextures();
    
    document.onkeydown = handlekeydown;
    document.onkeyup = handlekeyup;
    
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.enable(gl.DEPTH_TEST);
    
    tick();    
}
